#include "config_parse.h"
#include <string.h>
#include <stdlib.h>

#if defined(_WIN32) || defined(WIN32)
#define strcasecmp  _stricmp
#define strncasecmp _strnicmp
#define strtok_r    strtok_s
#endif

extern int config_open_xml(config_t* conf, const char* file);
extern int config_parse_xml(config_t* conf, const char* data, int size);
extern int config_open_json(config_t* conf, const char* file);
extern int config_parse_json(config_t* conf, const char* data, int size);
extern int config_open_yml(config_t* conf, const char* file);
extern int config_parse_yml(config_t* conf, const char* data, int size);
extern int config_open_ini(config_t* conf, const char* file);
extern int config_parse_ini(config_t* conf, const char* data, int size);

int config_open(config_t** conf, config_type_file type, const char* file) {
    int ret = -1;
    config_t *tmp = (config_t*)malloc(sizeof(config_t));
    tmp->first_node = NULL;
    tmp->ncase = 0;
    if(type == config_type_xml) {
        ret = config_open_xml(tmp, file);
    } else if(type == config_type_json) {
        ret = config_open_json(tmp, file);
    } else if(type == config_type_yml) {
        ret = config_open_yml(tmp, file);
    } else if(type == config_type_ini) {
        ret = config_open_ini(tmp, file);
    }
    if(ret != 0) {
        free(tmp);
    } else {
        *conf = tmp;
    }
    return ret;
}

int config_parse(config_t** conf, config_type_file type, const char* data, int size) {
    int ret = -1;
    config_t *tmp = (config_t*)malloc(sizeof(config_t));
    tmp->first_node = NULL;
    tmp->ncase = 0;
    if(type == config_type_xml) {
        return config_parse_xml(tmp, data, size);
    } else if(type == config_type_json) {
        return config_parse_json(tmp, data, size);
    } else if(type == config_type_yml) {
        return config_parse_yml(tmp, data, size);
    } else if(type == config_type_ini) {
        return config_parse_ini(tmp, data, size);
    }
    if(ret != 0) {
        free(tmp);
    } else {
        *conf = tmp;
    }
    return -1;
}

static void free_node(conf_node_t* node) {
    conf_node_t *tmp = node;
    while(tmp) {
        conf_node_t *p = tmp;
        free_node(tmp->first_child);
        tmp = tmp->next;

        free(p->name);
        free(p->value);
        free(p);
    }
}

void config_close(config_t *conf) {
    free_node(conf->first_node);
    free(conf);
}

int config_ncase(config_t *conf, int ncase) {
    conf->ncase = ncase;
}

static char* config_get_value(config_t *conf, char *path) {
    char *p, *o, *pv;
    conf_node_t *final_node;
    int final_find = 0;
    conf_node_t *node = conf->first_node;
    size_t path_len = strlen(path);
    char *path_value = (char*)malloc(path_len + 1);
    strcpy(path_value, path);
    path_value[path_len] = 0;
    pv = path_value;
    while ((p = strtok_r(pv, ".", &o)) != NULL) {
        conf_node_t *tmp;
        int find = 0;
        pv = NULL;
        for(tmp = node; tmp != NULL; tmp = tmp->next) {
            if((conf->ncase && !strcasecmp(p, tmp->name)) || (!conf->ncase && !strcmp(p, tmp->name))) {
                find = 1;
                break;
            }
        }
        if(find == 0) {
            final_find = 0;
            break;
        } else {
            final_find = 1;
            final_node = tmp;
            node = tmp->first_child;
        }
    }
    free(path_value);

    if(final_find) {
        return final_node->value;
    }

    return NULL;
}

char* config_get_string(config_t *conf, char *path, char *default_value) {
    char *ret = config_get_value(conf, path);
    if(ret)
        return ret;
    return default_value;
}

int32_t config_get_int(config_t *conf, char *path, int32_t default_value) {
    char *ret = config_get_value(conf, path);
    if(ret)
        return atoi(ret);
    return default_value;
}

int64_t config_getint64(config_t *conf, char *path, int64_t default_value) {
    char *ret = config_get_value(conf, path);
    if(ret)
        return _atoi64(ret);
    return default_value;
}

float config_get_float(config_t *conf, char *path, float default_value) {
    char *ret = config_get_value(conf, path);
    if(ret)
        return atof(ret);
    return default_value;
}

double config_get_double(config_t *conf, char *path, double default_value) {
    char *ret = config_get_value(conf, path);
    if(ret)
        return atof(ret);
    return default_value;
}